library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity sram_streamer IS
  PORT( 
       clk                : IN std_logic;  
       
       -- from command decoder
       datain             : IN unsigned(7 downto 0);  
       datain_valid       : IN std_logic;
       module_enable	     : IN std_logic; 
       
       -- sram grant
       grant              : IN std_logic;  
       grant_valid        : IN std_logic;
       -- sram bus
       sram_read_addr     : in std_logic_vector(17 DOWNTO 0);
       sram_read_data     : in std_logic_vector(15 DOWNTO 0);
       sram_read_valid    : IN std_logic;
       -- sram request
       request_sram      : out std_logic;
       request_addr      : out std_logic_vector(17 DOWNTO 0);  
       
       -- framebuilder interface
       frame_rden        : in std_logic;
       grant_frame       : in std_logic;
       grant_frame_valid : in std_logic;
       request_frame     : out std_logic;
       data_to_frame     : out std_logic_vector(7 DOWNTO 0);
       frame_wren        : out std_logic
               
    );
end sram_streamer;



architecture Behavioral of sram_streamer is
   
  signal safety_counter : unsigned(1 downto 0) := "10";   -- accept only every 3rd command as start
   
  signal words_to_fetch : unsigned(4 downto 0) := (others => '0'); 
  signal words_fetched : unsigned(4 downto 0) := (others => '0'); 
  signal addr_pointer : unsigned(17 downto 0) := (others => '0');
  
  signal value_buffer : std_logic_vector(15 downto 0) := (others => '0');
  
  signal sram_read_addr_buffer  : std_logic_vector(17 DOWNTO 0);
  signal sram_read_data_buffer  : std_logic_vector(15 DOWNTO 0);
  signal sram_read_valid_buffer : std_logic;
   
  TYPE STATE_TYPE IS (
      idle,
      get_addr2,
      get_addr3,
      get_frame_slot,
      send_addr_high_amount,
      send_addr_middle,
      send_addr_low,
      request,
      granted,
      send_high,
      send_low
   );
 
   SIGNAL state : STATE_TYPE := idle; 
    
begin
        

 
process (clk)
begin
  if rising_edge(clk) then
  
    -- local copy -> fmax
	sram_read_addr_buffer  <= sram_read_addr;
	sram_read_data_buffer  <= sram_read_data;
	sram_read_valid_buffer <= sram_read_valid;
    
    if datain_valid='1' and module_enable='1' then
		  safety_counter <= safety_counter +1;
		  if safety_counter = "10" then
			 safety_counter <= (others => '0');
		  end if;
	end if;
    
    case state is
    
      when idle =>
        if datain_valid='1' and module_enable='1' and safety_counter(1)='1' then
          state <= get_addr2;
          words_to_fetch <= datain(7 downto 3);
          addr_pointer(17 downto 16) <= datain(1 downto 0);
        end if; 
        
      when get_addr2 =>
        if datain_valid='1' and module_enable='1' then
          state <= get_addr3;
          addr_pointer(15 downto 8) <= datain;
        end if;  
    
      when get_addr3 =>
        if datain_valid='1' and module_enable='1' then
          state <= get_frame_slot;
          addr_pointer(7 downto 0) <= datain;
        end if;  
        
      when get_frame_slot =>
        if grant_frame='1' and grant_frame_valid='1' then
          state <= send_addr_high_amount;
        end if; 
        
      when send_addr_high_amount =>
		if frame_rden='1' then
			state <= send_addr_middle;
		end if;
		
	  when send_addr_middle =>
		if frame_rden='1' then
			state <= send_addr_low;
		end if;
		
	  when send_addr_low =>
		if frame_rden='1' then
			state <= request;
		end if;
    
      when request =>
        if grant='1' and grant_valid='1' then
          state<=granted;  
        end if;
      
      when granted =>
        if sram_read_addr_buffer =std_logic_vector(addr_pointer) and sram_read_valid_buffer ='1' then
          value_buffer <= sram_read_data_buffer;
          state<=send_high; 
        end if;
       
      when send_high =>
        if frame_rden='1' then
          state<=send_low; 
          words_fetched <= words_fetched + 1;
          addr_pointer <= addr_pointer + 1;
        end if;  
       
      when send_low =>
        if frame_rden='1' then
          if words_fetched < words_to_fetch then
            state<=request;
          else
            state <= idle;
            words_fetched <= (others => '0');
          end if; 
        end if;   
    
    end case;
    
  end if;
end process;

  with state select data_to_frame <=
    value_buffer(15 downto 8) when send_high,
    value_buffer(7 downto 0) when send_low,
    std_logic_vector(words_to_fetch) & '0' & std_logic_vector(addr_pointer(17 downto 16)) when send_addr_high_amount,
    std_logic_vector(addr_pointer(15 downto 8)) when send_addr_middle,
    std_logic_vector(addr_pointer(7 downto 0)) when send_addr_low,
    "00" & std_logic_vector(words_to_fetch+1) & '1' when others; 
    

  request_frame     <= '1' when state=get_frame_slot else '0';
  
  
  frame_wren        <= '1' when ((state=send_high or state=send_low 
								or state=send_addr_high_amount or state=send_addr_middle or state=send_addr_low) 
								and frame_rden='1') else '0';

  request_sram <= '1' when state=request else '0';
  request_addr <= std_logic_vector(addr_pointer);
  


end Behavioral;























